<!--
 * @Description: 图表
 * @Author bianpengfei
 * @create 2022/1/11 17:07
 -->
<!--default-->
<script>
export default { name: 'chart' }
</script>

<!--setup-->
<script setup>
import { computed, nextTick, onMounted, onUnmounted, reactive, ref, getCurrentInstance, watch } from 'vue'
import { Base, MathUtils } from '@bianpengfei/utils'
import { merge } from 'lodash'
import { formatNum, formatAxisLabel } from '/src/libs/utils/common'
let _chart = null // 图表实例
let _debounceResize = null // 去抖方法
const { proxy } = getCurrentInstance()
const _this = proxy

const props = defineProps({
  type: {
    type: String,
    default: '', // 图表类型
    required: true
  },
  title: {
    // 标题
    type: String,
    default: ''
  },
  height: {
    // 高度
    type: String,
    default: '300px'
  },
  data: {
    // 数据
    type: Array,
    default: () => []
  },
  dataProp: {
    // data值属性映射
    type: Object,
    default: () => ({
      name: 'name',
      value: 'value',
      value2: 'value2'
    })
  },
  chartOptions: {
    // chart Options
    type: Object,
    default: () => ({})
  },
  extraConfig: {
    // 额外的配置
    type: Object,
    default: () => ({
      xAxisIsDate: true, // x轴是否是日期
      yAxisValueShow: true, // 值文本是否展示
      y2AxisValueShow: true, // 比例文字是否展示
      yAxisUnit: '元', // y轴单位
      legend: ['x轴', '值'],
      xScroll: false, // x轴是否滚动
      colors: [
        '#9E87FF',
        '#FDD56A',
        '#FDB36A',
        '#FD866A',
        '#1890FF',
        '#13C2C2',
        '#2FC25B',
        '#FACC14',
        '#F04864',
        '#8543E0',
        '#3436C7',
        '#223273'
      ]
    })
  }
})
defineEmits({})
const state = reactive({
  options: {}
})
const chartRef = ref(null)

const isShowChartC = computed(() => !!props.data.length) //是否显示图表

const xAxisC = computed(() => props.data.map(v => v[props.dataProp.name])) // x轴

const yAxisC = computed(() => props.data.map(v => +v[props.dataProp.value] || 0)) // y轴

const y2AxisC = computed(() => {
  const isStack = props.data.some(v => Object.prototype.hasOwnProperty.call(v, props.dataProp.value2))
  if (isStack) {
    return props.data.map(v => +v[props.dataProp.value2] || 0)
  }
  const sum = MathUtils.sum(...yAxisC.value)
  return yAxisC.value.map(v => MathUtils.div(v, +sum))
}) // y轴右侧

const extraConfigC = computed(() =>
  merge(
    {
      xAxisIsDate: true, // x轴是否是日期
      yAxisValueShow: true, // 值文本是否展示
      y2AxisValueShow: true, // 比例文字是否展示
      yAxisUnit: '元', // y轴单位
      legend: ['x轴', '值'],
      xScroll: false, // x轴是否滚动
      colors: [
        '#9E87FF',
        '#FDD56A',
        '#FDB36A',
        '#FD866A',
        '#1890FF',
        '#13C2C2',
        '#2FC25B',
        '#FACC14',
        '#F04864',
        '#8543E0',
        '#3436C7',
        '#223273'
      ]
    },
    props.extraConfig
  )
) // 额外参数

watch([() => props.chartOptions, () => props.data], () => onChangeData())

onMounted(() => {
  // console.log(_this.$echarts)
  _debounceResize = Base.debounce(onChangeData, 200)
  window.addEventListener('resize', _debounceResize, false)
  onChangeData()
})

onUnmounted(() => {
  window.removeEventListener('resize', _debounceResize, false)
})

/**
 * 尺寸发生变化
 */
const onResize = () => {
  if (_chart) _chart.resize()
}

/**
 * 改变echarts 配置项
 */
const onChangeData = () => {
  if (!isShowChartC.value) {
    _chart = null
    return
  }
  const conf = {
    column: handlerColumn.bind(null),
    bar: handlerBar.bind(null),
    pie: handlerPie.bind(null),
    line: handlerLine.bind(null)
  }
  const option = conf?.[props.type]?.() || {}
  nextTick(() => {
    if (!_chart) _chart = _this.$echarts.init(chartRef.value)
    state.options = merge(option, props.chartOptions)
    // console.log(state.options)
    _chart.setOption(state.options)
  })
}

/**
 * 处理图形类型是 column
 * @returns {{yAxis: [{name: *, splitLine: {show: boolean}, type: string}, {name: *, splitLine: {show: boolean}, show: boolean, type: string}], xAxis: [{axisLabel: {rotate: number}, data: *, splitLine: {show: boolean}, type: string}], legend: {data: *[], selectedMode: boolean}, grid: {left: number}, series: [(*&{data: *, name: *, itemStyle: {color: *}, type: string}), (*&{data: *[], name: *, itemStyle: {color: (*|string)}, type: string, yAxisIndex: number})]}}
 */
function handlerColumn() {
  const { xAxisIsDate, yAxisValueShow, y2AxisValueShow, colors, legend, yAxisUnit, xScroll } = extraConfigC.value
  const isStack = props.data.some(v => Object.prototype.hasOwnProperty.call(v, props.dataProp.value2)) // 是否累加柱状
  const isDate = xAxisIsDate || !xAxisC.value.some(v => new Date(v).toString() === 'Invalid Date')
  return {
    legend: {
      selectedMode: true, // 可点击
      data: legend
    },
    grid: {
      left: 50
    },
    xAxis: [
      {
        type: 'category',
        data: xAxisC.value,
        axisTick: {
          alignWithLabel: true
        },
        axisLabel: {
          show: true,
          fontSize: 8,
          rotate: isDate ? 30 : 0,
          interval: isDate ? 0 : 'auto',
          formatter: text => (xAxisIsDate ? text : formatAxisLabel(text, 3))
        },
        splitLine: {
          // 不显示分割线
          show: false
        }
      }
    ],
    yAxis: [
      {
        // 左侧y轴
        name: legend[0],
        type: 'value',
        nameTextStyle: {
          align: 'left',
          fontSize: 10
        },
        splitNumber: 4,
        splitLine: {
          // 显示分割线
          show: true
        },
        ...(Math.max(...yAxisC.value) < 5
          ? {
              min: 0,
              max: 5
            }
          : {}),
        axisLabel: {
          fontSize: 10,
          formatter: data => {
            return `${data}${yAxisUnit}`
          }
        }
      },
      {
        // 右侧y轴
        show: legend.length > 1 && !isStack,
        name: legend[1],
        type: 'value',
        nameTextStyle: {
          align: 'right',
          fontSize: 10
        },
        // splitNumber: 4,
        ...(Math.max(...y2AxisC.value) < 1
          ? {
              min: 0,
              max: 1
            }
          : {}),
        max: 1,
        min: 0,
        splitLine: {
          show: true,
          lineStyle: {
            type: 'dotted'
          }
        },
        axisLabel: {
          fontSize: 10,
          formatter: data => {
            return `${((+data || 0) * 100).toFixed(0)}%`
          }
        }
      }
    ],
    series: [
      {
        name: legend[0],
        type: 'bar',
        data: yAxisC.value,
        itemStyle: {
          color: colors[0]
        },
        barMaxWidth: '18',
        label: {
          show: yAxisValueShow,
          textStyle: {
            color: '#333'
          },
          fontSize: 8,
          formatter: data => {
            let value = +data.value || 0
            if (String(data.value).includes('.')) value = (+data.value || 0).toFixed(2)
            return `${value}${yAxisUnit == '%' ? '%' : ''}`
          }
        },
        ...(isStack ? { stack: 'total' } : {})
      },
      {
        name: legend[1],
        type: 'line',
        yAxisIndex: 1, // 索引从0开始
        data: y2AxisC.value,
        itemStyle: {
          color: colors[1] || '#07c160'
        },
        label: {
          show: y2AxisValueShow,
          textStyle: {
            color: '#333'
          },
          fontSize: 8,
          formatter: data => {
            let value = +data.value || 0
            if (String(data.value).includes('.')) value = (+data.value || 0).toFixed(2)
            if (isStack) return `${value}${yAxisUnit == '%' ? '%' : ''}`
            return `${(value * 100).toFixed(2)}%`
          }
        },
        ...(isStack ? { stack: 'total', type: 'bar' } : {})
      }
    ],
    dataZoom: [
      {
        show: xScroll,
        height: 20,
        xAxisIndex: [0],
        bottom: 0,
        // handleSize: '100%',
        handleStyle: {
          color: colors[0]
        },
        textStyle: {
          color: 'rgb(108,175,201)'
        },
        fillerColor: 'rgb(179, 216, 255)',
        borderColor: 'rgba(204,187,225,0.5)'
      },
      {
        type: 'inside',
        show: true,
        height: 15,
        start: 1,
        end: 35
      }
    ]
  }
}

/**
 * 处理图形类型是 bar
 * */
function handlerBar() {
  const { xAxisIsDate, yAxisValueShow, y2AxisValueShow, colors, legend, yAxisUnit, xScroll } = extraConfigC.value
  return {
    color: colors,
    tooltip: {
      show: true,
      trigger: 'axis',
      formatter: val => {
        const [item0] = val
        return `${item0.axisValueLabel}：${item0.data}${yAxisUnit || ''}`
      },
      backgroundColor: 'rgba(0,0,0,0.7)', //设置背景图片 rgba格式
      borderWidth: 0,
      padding: [4, 8],
      axisPointer: {
        // 坐标轴指示器，坐标轴触发有效
        type: 'shadow' // 默认为直线，可选为：'line' | 'shadow'
      },
      textStyle: {
        color: '#fff', //设置文字颜色
        fontSize: 8
      }
    },
    grid: {
      left: '2%',
      right: '30',
      bottom: '30',
      top: '20',
      containLabel: true
    },
    xAxis: {
      type: 'value',
      splitNumber: 2,
      ...(Math.max(...yAxisC.value) < 5
        ? {
            min: 0,
            max: 5
          }
        : {}),
      axisLabel: {
        fontSize: 10,
        formatter: '{value}' + yAxisUnit || ''
      },
      boundaryGap: true,
      splitLine: {
        show: true,
        lineStyle: {
          type: 'dashed'
        }
      }
    },
    yAxis: {
      type: 'category',
      data: xAxisC.value,
      axisTick: {
        alignWithLabel: true,
        show: false
      },
      axisLine: {
        show: false
      },
      axisLabel: {
        fontSize: 10,
        show: true,
        formatter: text => formatAxisLabel(text, 7)
      }
    },
    series: [
      {
        name: yAxisUnit,
        type: 'bar',
        barMaxWidth: '12',
        data: yAxisC.value,
        showBackground: true,
        itemStyle: {
          color: new _this.$echarts.graphic.LinearGradient(1, 0, 0, 1, [
            { offset: 0, color: '#83bff6' },
            { offset: 0.5, color: '#188df0' },
            { offset: 1, color: '#188df0' }
          ]),
          borderRadius: [0, 5, 5, 0]
        },
        label: {
          show: true,
          fontSize: 10,
          position: 'insideLeft',
          borderType: 'dotted',
          fontStyle: 'italic',
          // background: this.colors[0],
          formatter: (value, a) => {
            return props.data[value.dataIndex].label
          }
        }
      }
    ]
  }
}

/**
 * 处理图形类型是 pie
 * */
function handlerPie() {
  const { xAxisIsDate, yAxisValueShow, y2AxisValueShow, colors, legend, yAxisUnit, xScroll } = extraConfigC.value
  return {
    tooltip: {
      show: true,
      trigger: 'item',
      formatter: '{b}：占比{d}%',
      backgroundColor: 'rgba(0,0,0,0.7)', //设置背景图片 rgba格式
      borderWidth: 0,
      padding: [4, 8],
      textStyle: {
        color: '#fff', //设置文字颜色
        fontSize: 8
      }
    },
    legend: {
      left: 'center',
      itemWidth: 8,
      itemHeight: 8,
      top: 10
    },
    series: [
      {
        name: legend[0],
        color: colors,
        type: 'pie',
        radius: ['26%', '42%'],
        center: ['50%', '55%'],
        data: props.data,
        clockwise: true,
        label: {
          show: true,
          fontSize: 10,
          // position: 'inner',
          // show: false,
          // margin: 10,
          // alignTo: 'edge',
          // position:'outer',
          // formatter: data => {
          //   console.log(data)
          //   let { name, percent, color } = data
          //   const label = formatAxisLabel(name, 4)
          //   return 'a'
          //       // `<div style="color: ${color}">${label}\n${percent}%'</div>`
          // },
          formatter: data => {
            let { name, percent, dataIndex } = data
            const label = formatAxisLabel(name, 4)
            return [
              `{${dataIndex}|${label}${formatNum(data.value, 2)}${yAxisUnit || ''}、}`,
              `{${dataIndex}|${percent}%}`
            ].join('\n')
          },
          textStyle: {
            color: '#333'
          },
          rich: colors.reduce((p, c, idx) => {
            p[String(idx)] = {
              fontSize: 10,
              color: c
            }
            return p
          }, {})
        },
        emphasis: {
          label: {
            show: true,
            fontWeight: 'normal'
          }
        },
        labelLine: {
          show: true,
          length: 2,
          normal: {
            length: 10,
            length2: 10,
            lineStyle: {
              width: 1
            }
          }
        }
      }
    ]
  }
}

/**
 * 处理图形类型是 line
 * */
function handlerLine() {
  const { xAxisIsDate, yAxisValueShow, y2AxisValueShow, colors, legend, yAxisUnit, xScroll } = extraConfigC.value
  return {
    grid: {
      left: '2%',
      right: '30',
      bottom: '3%',
      top: '10%',
      containLabel: true
    },
    tooltip: {
      trigger: 'axis'
    },
    xAxis: {
      type: 'category',
      data: xAxisC.value,
      axisLabel: {
        fontSize: 10,
        rotate: xAxisC.value.length > 3 || xAxisIsDate ? 45 : 0
      },
      boundaryGap: true
    },
    legend: {
      left: 'center',
      // itemWidth: 17,
      // itemHeight: 17,
      top: 10,
      data: legend
    },
    yAxis: {
      type: 'value',
      splitNumber: 3,
      // boundaryGap: true,
      axisLabel: {
        fontSize: 10,
        formatter: '{value}' + yAxisUnit
      }
    },
    dataZoom: [
      {
        show: xScroll,
        height: 20,
        xAxisIndex: [0],
        bottom: 0,
        // handleSize: '100%',
        handleStyle: {
          color: colors[0]
        },
        textStyle: {
          color: 'rgb(108,175,201)'
        },
        fillerColor: 'rgb(179, 216, 255)',
        borderColor: 'rgba(204,187,225,0.5)'
      },
      {
        type: 'inside',
        show: true,
        height: 15,
        start: 1,
        end: 35
      }
    ],
    series: [
      {
        name: legend[0],
        data: yAxisC.value,
        type: 'line',
        markPoint: {
          // data: [
          //   {type: 'max', name: '最大值'},
          //   {type: 'min', name: '最小值'}
          // ]
        }
      },
      {
        name: legend[1],
        data: y2AxisC.value,
        type: 'line',
        markPoint: {
          // data: [
          //   {type: 'max', name: '最大值'},
          //   {type: 'min', name: '最小值'}
          // ]
        }
      }
    ]
  }
}
</script>

<!--render-->
<template>
  <div class="chart__wrap">
    <g-title v-if="title">{{ title }}</g-title>
    <transition v-if="isShowChartC" name="fade" appear>
      <div :style="{ height: `${props.height}` }" ref="chartRef"></div>
    </transition>
    <template v-else>
      <g-empty
        :style="{
          width: '100%',
          background: `#fff`,
          height: `${props.height}`
        }"
      ></g-empty>
    </template>
  </div>
</template>

<!--style-->
<style scoped lang="less">
.chart__wrap {
  background: #fff;
  padding: 0.1rem;
}

/* fade */
.fade-enter-active {
  transition: opacity 0.28s;
}

.fade-enter-from,
.fade-leave-active {
  opacity: 0;
}
</style>
